import element.base.Word;
import element.base.WordType;

import java.util.ArrayList;

import static java.lang.Character.*;

public class Lexer {
    private String procedure;
    private ArrayList<Word> words;
    private int pointer;
    private char nowChar;

    public Lexer(String procedure) {
        this.procedure = procedure;
        this.words = new ArrayList<>();
        this.pointer = 0;
    }

    public void Execute() {
        getch();
        while (pointer < procedure.length()) {
            getSymbol();
        }
    }

    public void getSymbol() {
        if (fitTK("Main")) {
            isTK("Main", WordType.MAINTK);
        } else if (fitTK("Class")) {
            isTK("Class", WordType.CLASSTK);
        } else if (fitTK("def")) {
            isTK("def", WordType.DEFTK);
        } else if (fitTK("constructor")) {
            isTK("constructor", WordType.CONSTRUCTTK);
        } else if (fitTK("const")) {
            isTK("const", WordType.CONSTTK);
        } else if (fitTK("int")) {
            isTK("int", WordType.INTTK);
        } else if (fitTK("char")) {
            isTK("char", WordType.CHARTK);
        } else if (fitTK("longlong")) {
            isTK("longlong", WordType.LLTK);
        } else if (fitTK("long")) {
            isTK("long", WordType.LONGTK);
        } else if (fitTK("string")) {
            isTK("string", WordType.STRINGTK);
        } else if (fitTK("func")) {
            isTK("func", WordType.FUNCTK);
        } else if (fitTK("double")) {
            isTK("double", WordType.BOOLTK);
        } else if (fitTK("bool")) {
            isTK("bool", WordType.BOOLTK);
        } else if (fitTK("AllAttrConstructor")) {
            isTK("AllAttrConstructor", WordType.AACTK);
        } else if (fitTK("NoAttrConstructor")) {
            isTK("NoAttrConstructor", WordType.NACTK);
        } else if (fitTK("break")) {
            isTK("break", WordType.BREAKTK);
        } else if (fitTK("continue")) {
            isTK("continue", WordType.CONTINUETK);
        } else if (fitTK("if")) {
            isTK("if", WordType.IFTK);
        } else if (fitTK("else")) {
            isTK("else", WordType.ELSETK);
        } else if (fitTK("while")) {
            isTK("while", WordType.WHILETK);
        } else if (fitTK("input")) {
            isTK("input", WordType.INPUTTK);
        } else if (fitTK("print")) {
            isTK("print", WordType.PRINTTK);
        } else if (fitTK("return")) {
            isTK("return", WordType.RETURNTK);
        } else if (fitTK("void")) {
            isTK("void", WordType.VOIDTK);
        } else if (fitTK("format")) {
            isTK("format", WordType.FORMATTK);
        } else if (fitTK("for")) {
            isTK("for", WordType.FORTK);
        } else if (fitTK("true")) {
            isTK("true", WordType.TRUETK);
        } else if (fitTK("false")) {
            isTK("false", WordType.FALSETK);
        } else if (nowChar == '=') {
            if (hasPFix('=')) {
                isSignal("==", WordType.EQL);
            } else {
                isSignal("=", WordType.ASSIGN);
            }
        } else if (nowChar == '+') {
            if (hasPFix('=')) {
                isSignal("+=", WordType.ADDASSIGN);
            } else {
                isSignal("+", WordType.ADD);
            }
        } else if (nowChar == '-') {
            if (hasPFix('=')) {
                isSignal("-=", WordType.MINUASSIGN);
            } else if (hasPFix('>')) {
                isSignal("->", WordType.ARROW);
            } else {
                isSignal("-", WordType.MINU);
            }
        } else if (nowChar == '*') {
            if (hasPFix('=')) {
                isSignal("*=", WordType.MULTASSIGN);
            } else {
                isSignal("*", WordType.MULT);
            }
        } else if (nowChar == '/') {
            if (hasPFix('=')) {
                isSignal("/=", WordType.DIVASSIGN);
            } else {
                isSignal("/", WordType.DIV);
            }
        } else if (nowChar == '%') {
            if (hasPFix('=')) {
                isSignal("%=", WordType.MODASSIGN);
            } else {
                isSignal("%", WordType.MOD);
            }
        } else if (nowChar == '<') {
            if (hasPFix('=')) {
                isSignal("<=", WordType.LEQ);
            } else {
                isSignal("<", WordType.LSS);
            }
        } else if (nowChar == '>') {
            if (hasPFix('=')) {
                isSignal(">=", WordType.GEQ);
            } else {
                isSignal(">", WordType.GRE);
            }
        } else if (nowChar == '!') {
            if (hasPFix('=')) {
                isSignal("!=", WordType.NEQ);
            } else {
                isSignal("!", WordType.NOT);
            }
        } else if (nowChar == '|') {
            isSignal("||", WordType.OR);
        } else if (nowChar == '&') {
            isSignal("&&", WordType.AND);
        } else if (nowChar == ';') {
            isSignal(";", WordType.SEMICN);
        } else if (nowChar == ',') {
            isSignal(",", WordType.COMMA);
        } else if (nowChar == '.') {
            isSignal(".", WordType.POINT);
        } else if (nowChar == '@') {
            isSignal("@", WordType.AT);
        } else if (nowChar == '(') {
            isSignal("(", WordType.LPARENT);
        } else if (nowChar == ')') {
            isSignal(")", WordType.RPARENT);
        } else if (nowChar == '[') {
            isSignal("[", WordType.LBRACK);
        } else if (nowChar == ']') {
            isSignal("]", WordType.RBRACK);
        } else if (nowChar == '{') {
            isSignal("{", WordType.LBRACE);
        } else if (nowChar == '}') {
            isSignal("}", WordType.RBRACE);
        } else if (nowChar == ':') {
            isSignal(":", WordType.COLON);
        } else if (nowChar == '"') {
            StringConst();
        } else if (nowChar == '\'') {
            CharConst();
        } else if (isDigit(nowChar)) {
            Number();
        } else if (isAlphabetic(nowChar) || nowChar == '_') {
            Ident();
        } else if (isWhitespace(nowChar)) {
            getch();
        }
    }

    //读取字符并记录
    public char getch() {
        return nowChar = procedure.charAt(pointer++);
    }

    public String setin(String content) {
        content += nowChar;
        getch();
        return content;
    }

    //回滚字符并记录
    public void rollback(int n) {
        for (int i = 0; i < n; i++) {
            pointer--;
        }
        nowChar = procedure.charAt(pointer - 1);
    }

    //判断是否为identifier_nondigit & digit
    public boolean isIdentElement(char ch) {
        return isDigit(ch) || isAlphabetic(ch) || ch == '_';
    }

    //Ident判断
    public void Ident() {
        String content = "";
        while (isIdentElement(nowChar)) {
            content = setin(content);
        }
        words.add(new Word(content, WordType.IDENFR));
    }

    //Number判断
    public void Number() {
        String content = "";
        boolean pointed = false;
        while (isDigit(nowChar) || nowChar == '.' && !pointed) {
            if (nowChar == '.') {
                pointed = true;
            }
            content = setin(content);
        }
        words.add(new Word(content, WordType.NUMBER));
    }

    public boolean inString(char ch) {
        return ch == 32 || ch == 33 || ch == 37 || ch >= 40 && ch <= 126;
    }

    //字符串判断
    public void StringConst() {
        String content = "";
        if (nowChar =='"') {
            content = setin(content);
        } else {
            rollback(1);
            return;
        }

        while (nowChar != '"') {
            content = setin(content);
        }

        if (nowChar =='"') {
            content = setin(content);
        }

        words.add(new Word(content, WordType.STRCON));
    }

    public void CharConst() {
        String content = "";
        if (nowChar =='\'') {
            content = setin(content);
        } else {
            rollback(1);
            return;
        }

        while (nowChar != '\'') {
            content = setin(content);
        }

        if (nowChar =='\'') {
            content = setin(content);
        }

        words.add(new Word(content, WordType.CHARCON));
    }

    public boolean fitTK(String token) {
        return pointer < procedure.length() - (token.length() - 2) &&
                procedure.substring(pointer - 1, pointer - 1 + token.length()).equals(token);
    }

    //关键字统一操作
    public void isTK(String token, WordType wordType) {
        String content = "";
        int n = token.length();

        for (int i = 0; i < n; i++) {
            content = setin(content);
        }

        if (isIdentElement(nowChar)) {
            rollback(n);
            Ident();
        } else {
            words.add(new Word(content, wordType));
        }
    }

    public boolean hasPFix(char pFix) {
        return pointer < procedure.length() && procedure.charAt(pointer) == pFix;
    }

    //符号统一操作
    public void isSignal(String signal, WordType wordType) {
        String content = "";
        int n = signal.length();
        for (int i = 0; i < n; i++) {
            content = setin(content);
        }
        words.add(new Word(content, wordType));
    }

    public ArrayList<Word> getWords() {
        return words;
    }
}
